
package rbfs


import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"unicode/utf8"
)



type fileEntry struct {
	wbuf *bytes.Buffer
	data []byte	
	cksum uint32
}


type RBfs struct {

	seccnt uint      	//
	
	// fatbl []uint16		// fat16 table...
	files map[string]*fileEntry
}



func (fp * fileEntry)Write(p []byte) (n int, err error)  {
	// fmt.Printf( "write, %v\n", len(p) )
	return fp.wbuf.Write( p )
}


func (fp * fileEntry)Close() error  {
	fp.data = bytes.Clone( fp.wbuf.Bytes() )
	fp.wbuf.Reset()
	return nil
}


func NewRBfs( scnt uint ) * RBfs  {
	bfs := new(RBfs)
	bfs.seccnt = scnt
	bfs.files = make(map[string]*fileEntry)
	return bfs
}


func (bfs * RBfs)Create( fnm string ) (io.WriteCloser, error)  {

	if !utf8.ValidString( fnm ) {
		return nil, fmt.Errorf( "file name invalid : %v", fnm )
	}

	// bytes length.
	if len(fnm) > 16 {
		return nil, fmt.Errorf( "file name length > 16 : %v", fnm )
	}

	if _, ok := bfs.files[fnm]; ok {
		return nil, fmt.Errorf( "file exist already : %v", fnm )
	}

	//
	fp := new(fileEntry)
	fp.wbuf = bytes.NewBuffer( nil )
	fp.cksum = 0
	bfs.files[fnm] = fp
	return fp, nil
}


func (bfs * RBfs)FlushTo( wfd io.WriteSeeker ) error {
	
	var sidx uint32
	var offs int
	var pads [512]byte

	// zero
	for i:=0; i<512; i++  {
		pads[i] = 0
	}


	// super block 固定在第一个扇区的开始 32 字节.
	// 这个扇区的剩余部分 480 字节可以容纳 15 个 director entry .

	// 计算 directory block 大小.
	ecnt := len(bfs.files)
	if ecnt <= 0 {
		return fmt.Errorf( "root_dir, entrys cnt zero.." )
	}

	// 每个 entry 需要 32 个字节, 每个扇区可以容纳 16 个 entry.
	// super_block 信息占用最开始一个 entry.
	blks := (ecnt + 1 + 15) / 16;
	rtdir := make( []byte, 512 * blks )

	//
	binary.LittleEndian.PutUint32( rtdir[0:], 0 );
	binary.LittleEndian.PutUint32( rtdir[4:], 0x12345678 );
	binary.LittleEndian.PutUint32( rtdir[8:], 0 );
	binary.LittleEndian.PutUint32( rtdir[12:], 0 );

	binary.LittleEndian.PutUint32( rtdir[16:], 0x200 );
	binary.LittleEndian.PutUint32( rtdir[20:], 0 );
	binary.LittleEndian.PutUint32( rtdir[24:], uint32(ecnt) );
	binary.LittleEndian.PutUint32( rtdir[28:], 0 );

	//
	wfd.Seek( int64(blks * 512), io.SeekStart )
	sidx = uint32( blks )
	offs = 32

	// 
	for fn, ent := range bfs.files  {

		// root_dir, entry..
		nbs := copy( rtdir[offs:], []byte(fn) )
		if nbs < 16  {
			// zero tail part.
			copy( rtdir[offs+nbs:offs+16], pads[:] )
		}

		binary.LittleEndian.PutUint32( rtdir[offs+16:], sidx );
		binary.LittleEndian.PutUint32( rtdir[offs+20:], uint32( len(ent.data) ) );
		binary.LittleEndian.PutUint32( rtdir[offs+24:], 0 );
		binary.LittleEndian.PutUint32( rtdir[offs+28:], ent.cksum );
		offs += 32

		//
		_, err := wfd.Write( ent.data )
		if err != nil  {
			return fmt.Errorf( "write file blk fail, %v", err )
		}

		nbs = len(ent.data)
		if nbs % 512 != 0 {
			wfd.Write( pads[: 512 - (nbs % 512)] )
		}

		sidx += uint32( (nbs + 511) / 512 )
	}

	//
	wfd.Seek( 0, io.SeekStart )
	wfd.Write( rtdir )
	return nil
}

